/*
 * UserModuleDescription.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */

package engine;

import java.util.*;
import middle.*;

/**
 * A 'user module' is a module whose inner structure
 * is accessible by the user (UDPs and modules). By contrast,
 * standard primitives do not have an 'internal structure',
 * and the links between ports are not made using intermediary
 * DataHolders.
 */
@SuppressWarnings("serial")
abstract class UserModuleDescription
extends DefaultNameSpaceDescription implements ModuleFactory
{
	UserModuleDescription (String name) {
		super(name);
	}

	/**
	 * Creates the links between ports and their corresponding expressions
	 */
	void linkModule(Module m, NameSpace parent,
			byte strength,
			ModuleInstanceDescription miDesc) throws ParseException {
		if (isTop()) return;
		try {
			miDesc.orderPorts(connectionList, connectionHash);
		} catch (ParseException pex) {
			throw new ParseException(parent.desc.toString(miDesc.lineNo) + pex);
		}
		try {
			int portIndex = 0;
			portLoop:
				for (Enumeration extern = miDesc.portsVector.elements(),
						intern = connectionList.elements();
				extern.hasMoreElements() || intern.hasMoreElements() ; portIndex++) {

					ConnectionDescription cd =
						(ConnectionDescription)intern.nextElement();
					try{
						cd.computeType(this);
					} catch (ParseException pex) {
						throw new ParseException (toString(cd.lineNo) + pex);
					}
					Monitor mon;
					ExpressionDescription exp =
						(ExpressionDescription)extern.nextElement();
					if(exp == null)continue portLoop; //unconnected port

					xConsole.debug("matching " + cd + " with " + exp);


					try{
						if((cd.type & PortDescription.output) != 0)
							//prepare exp for continuous assignement
							try{
								((AssignableSelection)exp).toContSelection(strength);  
							}catch(ClassCastException ex){
								throw new ParseException("lValue required");
							}

							if((cd.type & PortDescription.input) != 0)
								//prepare port for cont. assign.
								cd.prepareForContSelection();

							if ((cd.type & PortDescription.inout) == PortDescription.inout) {
								mon =
									MBiContAssignMonitor.
									createNewMBiContAssignMonitor((WireSelection) cd.
											instantiate(m, null),
											(WireSelection)
											exp.instantiate(parent));
								mon =
									MBiContAssignMonitor.
									createNewMBiContAssignMonitor((WireSelection) exp.instantiate(parent),
											(WireSelection) cd.
											instantiate(m, null));
							} else {
								if ((cd.type & PortDescription.input) != 0) {
									mon =
										MContAssignMonitor.
										createNewMContAssignMonitor((LeftValue) cd.instantiate(m, null),
												exp.instantiate(parent));
								} else {
									mon =
										MContAssignMonitor.
										createNewMContAssignMonitor((LeftValue) exp.instantiate(parent),
												(Expression) cd.instantiate(m, null));
								}
							}
					} catch (ParseException pex) {
						xConsole.dumpStack(pex);
						throw new ParseException (parent.desc.toString(miDesc.lineNo) +
								": error: bad expression for port #"
								+ portIndex + ": "+ pex);
					}
				}
		}catch(NoSuchElementException ex){
			//oops, the port lists don't match !
			xConsole.dumpStack(ex);
			throw new ParseException(parent.desc.toString(miDesc.lineNo) +
			": error : port lists don't match");
		}
	}

	void linkModuleArray(Module[] modules, NameSpace parent,
			ModuleInstanceDescription miDesc,
			byte strength,
			int ars, int are,
			int n, int increment)
	throws ParseException
	{
		try {
			miDesc.orderPorts(connectionList, connectionHash);
		} catch (ParseException pex) {
			throw new ParseException(parent.desc.toString(miDesc.lineNo) + pex);
		}
		try {
			//fixing ports
			int portIndex = 0;
			portloop:
				for (Enumeration extern = miDesc.portsVector.elements(),
						intern = connectionList.elements();
				extern.hasMoreElements() || intern.hasMoreElements(); portIndex++) {
					ConnectionDescription cd = (ConnectionDescription) intern.nextElement();
					try{
						cd.computeType(this);
					} catch (ParseException pex) {
						throw new ParseException (toString(cd.lineNo) + pex);
					}
					ExpressionDescription externExpDesc =
						(ExpressionDescription) extern.nextElement();

					if (externExpDesc == null) continue portloop;

					Monitor mon;
					try {
						if ((cd.type & PortDescription.output) != 0) {
							xConsole.debug("output port");
							try {
								((AssignableSelection)externExpDesc).toContSelection();
							} catch (ClassCastException cex) {
								throw new ParseException("lValue requierd");
							}
						}

						if ((cd.type & PortDescription.input) != 0)
							cd.prepareForContSelection();

						Expression externExpression = externExpDesc.instantiate(parent);
						Expression internExpression =
							(Expression) cd.instantiate(modules[0], null);

						if (externExpression.length == internExpression.length) {
							for (int mi = 0; mi < n ; mi++) {
								if (mi != 0) {
									internExpression =
										(Expression) cd.instantiate(modules[mi], null);
									externExpression = externExpDesc.instantiate(parent);
								}
								if ((cd.type & PortDescription.inout) == PortDescription.inout) {
									mon =
										MBiContAssignMonitor.
										createNewMBiContAssignMonitor
										((WireSelection) internExpression,
												(WireSelection) externExpression);
									mon =
										MBiContAssignMonitor.
										createNewMBiContAssignMonitor
										((WireSelection) externExpDesc.instantiate(parent),
												(WireSelection) cd.instantiate(modules[mi], null));
								} else {
									if ((cd.type & PortDescription.output) != 0) {
										mon = MContAssignMonitor.createNewMContAssignMonitor
										((LeftValue) externExpression, internExpression);
									} else {
										mon = MContAssignMonitor.createNewMContAssignMonitor
										((LeftValue) internExpression, externExpression);
									}
								}
							}
						} else {
							if ((externExpression.length / internExpression.length) == n) {
								//here, at last, slices:
									if ((cd.type & PortDescription.inout) == PortDescription.inout) {
										WireSelection slices[] = new WireSelection[n];
										slices[0] = (WireSelection)internExpression;
										for (int mi = 1; mi < n; mi++)
											slices[mi] = (WireSelection) cd.instantiate(modules[mi],
													null);
										mon = MBiContAssignMonitor.createNewMBiContAssignMonitor
										(slices, (WireSelection) externExpression);

										for (int mi = 0; mi < n; mi++)
											slices[mi] = (WireSelection) cd.instantiate(modules[mi],
													null);
										mon = MBiContAssignMonitor.createNewMBiContAssignMonitor
										((WireSelection)externExpDesc.instantiate(parent), slices);
									} else {
										if ((cd.type & PortDescription.input) != 0) {
											LeftValue slices[] = new LeftValue[n];
											Expression out[] = new Expression[1];
											out[0] = externExpression;
											slices[0] = (LeftValue)internExpression;
											for (int mi = 1; mi < n; mi++) {
												slices[mi] = (LeftValue) cd.instantiate(modules[mi],
														null);
											}
											mon = new MContAssignMonitor(slices, out);
										} else {
											Expression slices[] = new Expression[n];
											LeftValue out[] = new LeftValue[1];
											out[0] = (LeftValue) externExpression;
											slices[0] = internExpression;
											for (int mi = 1; mi < n; mi++) {
												slices[mi] = (Expression) cd.instantiate(modules[mi],
														null);
											}
											mon = new MContAssignMonitor(out, slices);
										}
									}
							} else
								throw new ParseException ("bad port expression length");
						}
					} catch (ParseException pex) {
						xConsole.dumpStack(pex);
						throw new ParseException (parent.desc.toString(miDesc.lineNo) +
								": error: bad expression for port #" +
								+ portIndex + ": " + pex);
					}
				}
		} catch (NoSuchElementException ex) {
			//oops, the port lists don't match !
			xConsole.dumpStack(ex);
			throw new ParseException(parent.desc.toString(miDesc.lineNo) +
			": error : port lists don't match");
		}
	}
}








